package com.jingdianjichi.subject.domain.service.impl;

import com.alibaba.fastjson.JSON;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.jingdianjichi.subject.common.enums.IsDeleteFlagEnum;
import com.jingdianjichi.subject.domain.convert.SubjectCategoryConvert;
import com.jingdianjichi.subject.domain.entity.SubjectCategoryBO;
import com.jingdianjichi.subject.domain.entity.SubjectLabelBO;
import com.jingdianjichi.subject.domain.util.CacheUtil;
import com.jingdianjichi.subject.infra.basic.entity.SubjectCategory;
import com.jingdianjichi.subject.infra.basic.entity.SubjectLabel;
import com.jingdianjichi.subject.infra.basic.entity.SubjectMapping;
import com.jingdianjichi.subject.infra.basic.service.SubjectCategoryService;
import com.jingdianjichi.subject.domain.service.SubjectCategoryDomainService;
import com.jingdianjichi.subject.infra.basic.service.SubjectLabelService;
import com.jingdianjichi.subject.infra.basic.service.SubjectMappingService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * 题目分类领域服务的实现类
 * 负责题目分类相关的业务处理
 *
 * @author: WuYimin
 * Date: 2024-02-03
 */
@Service
@Slf4j
public class SubjectCategoryDomainServiceImpl implements SubjectCategoryDomainService {

	@Resource // 注入题目分类的基础服务对象
	private SubjectCategoryService subjectCategoryService;

	@Resource
	private SubjectMappingService subjectMappingService;

	@Resource
	private SubjectLabelService subjectLabelService;

	@Resource
	private ThreadPoolExecutor labelThreadPool;
	
	@Resource
	private Cache<String,String> localCache;

	@Resource
	private CacheUtil cacheUtil;

	/**
	 * 添加题目分类
	 *
	 * @param subjectCategoryBo
	 */
	public void add(SubjectCategoryBO subjectCategoryBo) {
		// 记录日志
		if (log.isInfoEnabled()) {
			log.info("SubjectCategoryController.add.dto:{}", JSON.toJSONString(subjectCategoryBo));
		}
		// 将业务对象转换为实体对象
		SubjectCategory subjectCategory = SubjectCategoryConvert.INSTANCE.convertBoTOCategory(subjectCategoryBo);
		// 设置该分类为未删除状态
		subjectCategory.setIsDeleted(IsDeleteFlagEnum.UN_DELETED.getCode());
		// 调用基础服务添加题目分类
		subjectCategoryService.insert(subjectCategory);
	}

	/**
	 * 查询题目分类
	 *
	 * @param subjectCategoryBo
	 * @return
	 */
	public List<SubjectCategoryBO> queryCategory(SubjectCategoryBO subjectCategoryBo) {
		// 将业务对象转换为实体对象
		SubjectCategory subjectCategory = SubjectCategoryConvert.INSTANCE.convertBoTOCategory(subjectCategoryBo);
		// 设置未删除标志
		subjectCategory.setIsDeleted(IsDeleteFlagEnum.UN_DELETED.getCode());
		// 调用基础服务查询题目分类
		List<SubjectCategory> subjectCategoryList = subjectCategoryService.queryCategory(subjectCategory);

		// 将实体对象列表转换为业务对象列表
		List<SubjectCategoryBO> boList = SubjectCategoryConvert.INSTANCE.convertCategoryToBoList(subjectCategoryList);

		// 记录日志
		if (log.isInfoEnabled()) {
			log.info("SubjectCategoryController.queryPrimaryCategory.boList:{}", JSON.toJSONString(boList));
		}
		boList.forEach(bo -> {
			Integer subjectCount = subjectCategoryService.querySubjectCount(bo.getId());
			bo.setCount(subjectCount);
		});

		return boList;
	}

	/**
	 * 更新题目分类
	 *
	 * @param subjectCategoryBo
	 * @return
	 */
	public Boolean update(SubjectCategoryBO subjectCategoryBo) {
		// 将业务对象转换为实体对象
		SubjectCategory subjectCategory = SubjectCategoryConvert.INSTANCE.convertBoTOCategory(subjectCategoryBo);
		// 调用基础服务更新题目分类
		int count = subjectCategoryService.update(subjectCategory);
		// 根据更新结果返回布尔值
		return count > 0;
	}

	/**
	 * 删除题目分类
	 *
	 * @param subjectCategoryBo
	 * @return
	 */
	public Boolean delete(SubjectCategoryBO subjectCategoryBo) {
		// 将业务对象转换为实体对象
		SubjectCategory subjectCategory = SubjectCategoryConvert.INSTANCE.convertBoTOCategory(subjectCategoryBo);
		// 设置删除标志
		subjectCategory.setIsDeleted(IsDeleteFlagEnum.DELETED.getCode());
		// 调用基础服务更新题目分类，实现逻辑删除
		int count = subjectCategoryService.update(subjectCategory);
		// 根据更新结果返回布尔值
		return count > 0;
	}



	///**
	// * 查询分类及标签的方法。首先，会尝试从本地缓存中获取信息，如果缓存中没有，则查询数据库获取数据，
	// * 查询完成后，将数据缓存起来以便下次使用。（本地缓存初始版本）
	// *
	// * @param subjectCategoryBo 接收的分类业务对象参数，用于指定查询的条件，如大类ID
	// * @return 返回包含分类及其标签的列表。每个分类都会附带其下属的标签信息，形成一个有层次的数据结构。
	// */
	//@SneakyThrows
	//@Override
	//public List<SubjectCategoryBO> queryCategoryAndLabel(SubjectCategoryBO subjectCategoryBo) {
	//	// 构造缓存的键，格式为"categoryAndLabel."加上分类的ID。这样确保缓存的唯一性和可查询性。
	//	String cacheKey = "categoryAndLabel." + subjectCategoryBo.getId();
	//	// 尝试从本地缓存中获取以该键为索引的数据
	//	String content = localCache.getIfPresent(cacheKey);
	//	// 用于存放查询结果的列表
	//	List<SubjectCategoryBO> subjectCategoryBOS = new LinkedList<>();
	//	// 检查缓存中是否已有数据
	//	if(StringUtils.isBlank(content)) {
	//		// 如果缓存中没有数据，调用getSubjectCategoryBOS方法查询数据库
	//		subjectCategoryBOS = getSubjectCategoryBOS(subjectCategoryBo.getId());
	//		// 将查询结果转换为JSON字符串后存入本地缓存，以便下次快速获取
	//		localCache.put(cacheKey, JSON.toJSONString(subjectCategoryBOS));
	//	} else {
	//		// 如果缓存中已存在数据，直接将JSON字符串转换回对象列表
	//		subjectCategoryBOS = JSON.parseArray(content, SubjectCategoryBO.class);
	//	}
	//	// 返回查询结果
	//	return subjectCategoryBOS;
	//}


	/**
	 * 查询分类及标签的方法。首先，会尝试从本地缓存中获取信息，如果缓存中没有，则查询数据库获取数据，
	 * 查询完成后，将数据缓存起来以便下次使用。（本地缓存优化后版本）
	 *
	 * @param subjectCategoryBo 接收的分类业务对象参数，用于指定查询的条件，如大类ID
	 * @return 返回包含分类及其标签的列表。每个分类都会附带其下属的标签信息，形成一个有层次的数据结构。
	 */
	@SneakyThrows
	@Override
	public List<SubjectCategoryBO> queryCategoryAndLabel(SubjectCategoryBO subjectCategoryBo) {
		Long id = subjectCategoryBo.getId();
		// 构造缓存的键，格式为"categoryAndLabel."加上分类的ID。这样确保缓存的唯一性和可查询性。
		String cacheKey = "categoryAndLabel." + subjectCategoryBo.getId();
		// 尝试从本地缓存中获取以该键为索引的数据
		List<SubjectCategoryBO> subjectCategoryBOS = cacheUtil.getResult(cacheKey,
				SubjectCategoryBO.class, (key) -> getSubjectCategoryBOS(id));
		// 返回查询结果
		return subjectCategoryBOS;
	}


	// 查询分类及标签（线程池版本）
	@SneakyThrows
	private List<SubjectCategoryBO> getSubjectCategoryBOS(Long categoryId) {
		// 根据传入的大类ID（parentId）查询所有未删除的子分类
		SubjectCategory subjectCategory = new SubjectCategory();
		subjectCategory.setParentId(categoryId);
		subjectCategory.setIsDeleted(IsDeleteFlagEnum.UN_DELETED.getCode());
		List<SubjectCategory> subjectCategoryList = subjectCategoryService.queryCategory(subjectCategory);

		// 如果启用了info级别的日志，则记录查询到的分类信息
		if (log.isInfoEnabled()) {
			log.info("SubjectCategoryController.queryCategoryAndLabel.subjectCategoryList:{}", JSON.toJSONString(subjectCategoryList));
		}

		// 将查询到的分类信息转换为业务对象列表
		List<SubjectCategoryBO> categoryBOList = SubjectCategoryConvert.INSTANCE.convertCategoryToBoList(subjectCategoryList);

		// 使用FutureTask异步处理，提高获取标签信息的效率
		List<FutureTask<Map<Long,List<SubjectLabelBO>>>> futureTaskList = new LinkedList<>();
		// 定义一个存储最终结果的Map，键为分类ID，值为对应的标签列表
		Map<Long,List<SubjectLabelBO>> map = new HashMap<>();
		// 对每个分类并发获取标签信息
		categoryBOList.forEach(category -> {
			FutureTask<Map<Long,List<SubjectLabelBO>>> futureTask = new FutureTask<>(() -> getLabelBOList(category));
			futureTaskList.add(futureTask);
			labelThreadPool.submit(futureTask); // 提交到线程池执行
		});
		// 等待所有异步任务完成，并汇总结果
		for (FutureTask<Map<Long, List<SubjectLabelBO>>> futureTask : futureTaskList) {
			Map<Long, List<SubjectLabelBO>> resultMap = futureTask.get(); // 阻塞等待结果
			if(CollectionUtils.isEmpty(resultMap)) {
				continue;
			}
			map.putAll(resultMap);
		}
		// 将标签信息设置到对应的分类业务对象中
		categoryBOList.forEach(categoryBO -> {
			categoryBO.setLabelBOList(map.get(categoryBO.getId()));
		});
		return categoryBOList;
	}

	/**
	 * 为指定分类获取标签列表
	 *
	 * @param category 分类业务对象
	 * @return 返回一个Map，包含分类ID和对应的标签业务对象列表
	 */
	private Map<Long, List<SubjectLabelBO>> getLabelBOList(SubjectCategoryBO category) {
		// 记录日志
		if (log.isInfoEnabled()) {
			log.info("getLabelBOList.dto:{}", JSON.toJSONString(category));
		}
		Map<Long, List<SubjectLabelBO>> labelMap = new HashMap<>();
		SubjectMapping subjectMapping = new SubjectMapping();
		subjectMapping.setCategoryId(category.getId());
		// 查询与分类关联的所有标签ID
		List<SubjectMapping> mappingList = subjectMappingService.queryLabelId(subjectMapping);
		if (CollectionUtils.isEmpty(mappingList)) {
			return null;
		}
		// 从关联信息中提取所有标签ID
		List<Long> lableIdList = mappingList.stream().map(SubjectMapping::getLabelId).collect(Collectors.toList());
		// 批量查询标签详细信息
		List<SubjectLabel> labelList = subjectLabelService.batchQueryById(lableIdList);
		List<SubjectLabelBO> labelBOList = new LinkedList<>();
		// 将标签信息转换为业务对象
		labelList.forEach(label -> {
			SubjectLabelBO subjectLabelBO = new SubjectLabelBO();
			subjectLabelBO.setId(label.getId());
			subjectLabelBO.setLabelName(label.getLabelName());
			subjectLabelBO.setCategoryId(label.getCategoryId());
			subjectLabelBO.setSortNum(label.getSortNum());
			labelBOList.add(subjectLabelBO);
		});
		labelMap.put(category.getId(), labelBOList);
		return labelMap;
	}

	/**
	 * 查询分类及标签 （线程池二次优化后版本）
	 *
	 * @param subjectCategoryBo 接收的分类业务对象参数，用于指定查询的条件，如大类ID
	 * @return 返回包含分类及其对应标签的列表，每个分类下包含其相关的标签信息
	 */
	//@SneakyThrows // 该注解用于自动处理方法内的受检异常，避免显式的try-catch语句
	//@Override
	//public List<SubjectCategoryBO> queryCategoryAndLabel(SubjectCategoryBO subjectCategoryBo) {
	//	// 创建一个新的SubjectCategory实例，设置其parentId为传入的大类ID，并设置删除标志为未删除
	//	SubjectCategory subjectCategory = new SubjectCategory();
	//	subjectCategory.setParentId(subjectCategoryBo.getId());
	//	subjectCategory.setIsDeleted(IsDeleteFlagEnum.UN_DELETED.getCode());
	//	// 调用service层的方法，根据条件查询所有匹配的分类
	//	List<SubjectCategory> subjectCategoryList = subjectCategoryService.queryCategory(subjectCategory);
	//
	//	// 如果info级别的日志已启用，则记录查询到的分类列表信息
	//	if (log.isInfoEnabled()) {
	//		log.info("queryCategoryAndLabel：{}", JSON.toJSONString(subjectCategoryList));
	//	}
	//
	//	// 将查询到的SubjectCategory列表转换为SubjectCategoryBO列表，用于业务层处理
	//	List<SubjectCategoryBO> categoryBOList = SubjectCategoryConvert.INSTANCE.convertCategoryToBoList(subjectCategoryList);
	//
	//	// 使用流处理，对每个分类进行异步处理，查询其下的标签列表
	//	List<CompletableFuture<Map<Long, List<SubjectLabelBO>>>> completableFutureList = categoryBOList.stream()
	//			.map(category -> CompletableFuture.supplyAsync(() -> getLabelBOList(category), labelThreadPool)
	//	).collect(Collectors.toList());
	//
	//	// 初始化一个HashMap，用于存储分类ID及其对应的标签列表
	//	Map<Long, List<SubjectLabelBO>> map = new HashMap<>();
	//	// 等待所有的CompletableFuture任务执行完毕，并收集结果
	//	completableFutureList.forEach(future -> {
	//		try {
	//			Map<Long, List<SubjectLabelBO>> resultMap = future.get(); // 获取异步任务的结果
	//			map.putAll(resultMap); // 将结果添加到最终的map中
	//		} catch (InterruptedException e) {
	//			throw new RuntimeException(e);
	//		} catch (ExecutionException e) {
	//			throw new RuntimeException(e);
	//		}
	//	});
	//	// 遍历分类业务对象列表，为每个分类设置其对应的标签列表
	//	categoryBOList.forEach(categoryBO -> {
	//		categoryBO.setLabelBOList(map.get(categoryBO.getId()));
	//	});
	//	return categoryBOList; // 返回包含分类及其标签的列表
	//}


}